package cn.kbt.dbdtobean.mvcbean;

import cn.kbt.dbdtobean.core.DBDToBeanContext;
import cn.kbt.dbdtobean.utils.DBDToBeanUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Kele-Bing
 * @version 1.0
 * @since 2021/9/21 23:09
 */
public class DBDToMapper extends AbstractDBDToMVC {

    protected final static String MAPPER_INTERFACE_NAME = "Mapper";
    protected final static String MAVEN_MAPPER_XML_HONE = "src\\main\\resources\\";
    protected final static String SIMPLE_MAPPER_XML_HONE = "src\\";
    protected static String interfacesName = null;
    private String entityName;
    private ResultSetMetaData resultSetMetaData;
    
    private DatabaseMetaData databaseMetaData;

    /**
     * 生成Mapper层接口目录以及内容
     *
     * @param createBeanName 文件名
     * @return 内容
     * @throws IOException IO 异常
     */
    public String mapperInterfaces(String createBeanName) throws IOException {
        createBeanName = DBDToBeanUtils._CharToUpperCase(createBeanName);
        DBDToMVCDefinition definition = DBDToBeanContext.getDbdToMVCDefinition();
        interfacesName = super.createInterfaces(definition, createBeanName, MAPPER_INTERFACE_NAME);
        return interfacesName;
    }

    /**
     * 生成Mapper层xml文件目录以及内容
     *
     * @param createBeanName 文件名
     * @throws IOException IO 异常
     */
    public void mapperXML(String createBeanName, String tableName) throws IOException {
        createBeanName = DBDToBeanUtils._CharToUpperCase(createBeanName);
        this.entityName = createBeanName;
        DBDToMVCDefinition definition = DBDToBeanContext.getDbdToMVCDefinition();
        File file = new File(System.getProperty("user.dir") + "\\" + definition.getModulesName() + "\\" + definition.getMapperPath() + DBDToBeanUtils.packageToPath(definition.getMapperXmlLocation()));
        boolean mkdir = file.mkdir();
        String createFileName = parseMVCName(definition, createBeanName, MAVEN_MAPPER_XML_HONE);
        file = new File(file + "\\" + createFileName + ".xml");
        FileWriter fw = new FileWriter(file);
        fw.write(createXmlStart(tableName));
        fw.flush();
        fw.close();
    }

    /**
     * 生成Mapper层xml文件开头配置
     *
     * @return 内容
     */
    public String createXmlStart(String tableName) {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<!DOCTYPE mapper\n\t\t" +
                xmlPublicAndHttp +
                ">\n" +
                "<mapper namespace = \"" +
                DBDToBeanContext.getDbdToMVCDefinition().getMapperLocation() + "." + interfacesName +
                "\">" + (DBDToBeanContext.getDbdToMVCDefinition().isGeneratecurd() ? "\n\n\t" + this.createXmlBeans(tableName) : "") +
                "\n\n</mapper>";
    }
    
    public String createXmlBeans(String tableName) {
        // 初始化获取数据库数据信息
        try {
            this.getMetaData(tableName);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return this.createResultMap(tableName) + this.createSqlLabel() + this.createXmlBeansCURD(tableName);
    }

    /**
     * 生成Mapper层xml文件CURD目录
     *
     * @return 内容
     */
    public String createXmlBeansCURD(String tableName) {
        
        return "<select id=\"query" + entityName + "ById\" " +
                " parameterType=\"" + 
                DBDToBeanContext.getDbdToMVCDefinition().getEntityLocation() + "." + entityName + "\">" +
                " resultType=\"" +
                DBDToBeanContext.getDbdToMVCDefinition().getEntityLocation() + "." + entityName + "\">" +
                "\n\t\t" + this.createQueryOne(tableName) +
                "\n\t</select>\n\n" + "\t" +

                "<select id=\"query" + entityName + "List\"" +
                " resultType=\"" +
                DBDToBeanContext.getDbdToMVCDefinition().getEntityLocation() + "." + entityName + "\">" +
                "\n\t\t" + this.createQueryList(tableName) + "\n\t</select>\n\n" + "\t" +

                "<insert id=\"insert" + entityName + "\"" +
                " parameterType=\"" +
                DBDToBeanContext.getDbdToMVCDefinition().getEntityLocation() + "." + entityName + "\">" +
                "\n\t\t" + this.createInsert(tableName) + "\n\t</insert>\n\n" + "\t" +

                "<update id=\"update" + entityName + "\"" +
                " parameterType=\"" +
                DBDToBeanContext.getDbdToMVCDefinition().getEntityLocation() + "." + entityName + "\">" +
                "\n\t\t" + this.createUpdate(tableName) + "\n\t</update>\n\n" + "\t" +

                "<delete id=\"delete" + entityName + "ById\"" +
                " parameterType=\"" +
                DBDToBeanContext.getDbdToMVCDefinition().getEntityLocation() + "." + entityName + "\">" +
                "\n\t\t" + this.createDelete(tableName) + "\n\t</delete>";
    }
    
    public String createResultMap(String tableName) {
        StringBuilder sb = new StringBuilder("<resultMap id=\"baseResultMap\" type=\"");
        sb.append(DBDToBeanContext.getDbdToMVCDefinition().getEntityLocation() + "." + entityName).append("\">").append("\n\t\t");
        return sb.toString();
    }
    
    public String createSqlLabel() {
        StringBuilder sb = new StringBuilder("\t<sql id=\"baseColumnList\">\n\t\t");
        try {
            if(DBDToBeanUtils.isNotEmpty(databaseMetaData)) {
                for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
                    sb.append(resultSetMetaData.getColumnName(i)).append(", ");
                    if(i == DBDToBeanContext.getDbdToMVCDefinition().getColumnNum()) {
                        sb.append("\n\t\t");
                    }
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        sb.delete(sb.length() - 2, sb.length());
        sb.append("\n\t</sql>");
        sb.append("\n\n\t");
        return sb.toString();
    }
    
    public String createQueryOne(String tableName) {
        if (DBDToBeanUtils.isNotEmpty(databaseMetaData)) {
            StringBuilder sb = new StringBuilder("select\n\t\t\t<include refid=\"baseColumnList\" />\n\t\t");
            sb.append("from ").append(tableName).append(" where ");
            try {
                List<String> primaryKeys = getPrimaryKeys(tableName);
                // 主键不存在
                if (DBDToBeanUtils.isEmpty(primaryKeys)) {
                    sb.append(resultSetMetaData.getColumnName(0)).append(" = #{").append(DBDToBeanUtils.parseFieldName(resultSetMetaData.getColumnName(0))).append("}, ");
                }
                // 主键存在
                for (String primaryKey : primaryKeys) {
                    sb.append(primaryKey).append(" = #{").append(DBDToBeanUtils.parseFieldName(primaryKey)).append("} and ");
                }
                sb.delete(sb.length() - 5, sb.length());
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
            return sb.toString();
        }
        return null;
    }
    
    public String createQueryList(String tableName) {
        return DBDToBeanUtils.isNotEmpty(databaseMetaData) ? "select\n\t\t\t<include refid=\"baseColumnList\" />" + "\n\t\tfrom " + tableName : null;
    }
    
    public String createInsert(String tableName) {
        if (DBDToBeanUtils.isNotEmpty(databaseMetaData)) {
            StringBuilder sb = new StringBuilder("insert into " + tableName + "\n\t\t<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n\t\t\t");
            try {
                for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
                    sb.append("<if test=").append("\"");
                    sb.append(DBDToBeanUtils.parseFieldName(resultSetMetaData.getColumnName(i)))
                            .append(" !=null\">").append("\n\t\t\t\t")
                            .append(resultSetMetaData.getColumnName(i))
                            .append(",").append("\n\t\t\t").append("</if>").append("\n\t\t\t");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            sb.delete(sb.length() - 1, sb.length());
            sb.append("</trim>").append("\n\t\t").append("<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\"\n\t\t\t>");
            try {
                for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
                    sb.append("<if test=").append("\"");
                    sb.append(DBDToBeanUtils.parseFieldName(resultSetMetaData.getColumnName(i)))
                            .append(" !=null\">").append("\n\t\t\t\t")
                            .append("#{")
                            .append(DBDToBeanUtils.parseFieldName(resultSetMetaData.getColumnName(i)))
                            .append("},").append("\n\t\t\t").append("</if>").append("\n\t\t\t");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            sb.delete(sb.length() - 1,sb.length());
            sb.append("</trim>");
            return sb.toString();
        }
        return null;
    }
    
    public String createUpdate(String tableName) {
        if (DBDToBeanUtils.isNotEmpty(databaseMetaData)) {
            StringBuilder sb = new StringBuilder("update " + tableName + "\n\t\t<set>");
            sb.append("\n\t\t\t").append("<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\"\n\t\t\t\t>");
            try {
                for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
                    sb.append("<if test=").append("\"");
                    sb.append(DBDToBeanUtils.parseFieldName(resultSetMetaData.getColumnName(i)))
                            .append(" !=null\">").append("\n\t\t\t\t\t")
                            .append(resultSetMetaData.getColumnName(i)).append(" = #{")
                            .append(DBDToBeanUtils.parseFieldName(resultSetMetaData.getColumnName(i)))
                            .append("},").append("\n\t\t\t\t").append("</if>").append("\n\t\t\t\t");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            sb.delete(sb.length() - 2,sb.length());
            sb.append("</trim>").append("\n\t\t").append("</set>");
            return sb.toString();
        }
        return null;
    }
    
    public String createDelete(String tableName) {
        StringBuilder sb = new StringBuilder( "delete from " + tableName + " where ");
        List<String> primaryKeys = getPrimaryKeys(tableName);
        try {
            // 主键不存在
            if (DBDToBeanUtils.isEmpty(primaryKeys)) {
                sb.append(resultSetMetaData.getColumnName(0)).append(" = #{").append(DBDToBeanUtils.parseFieldName(resultSetMetaData.getColumnName(0))).append("}, ");
            }
            // 主键存在
            for (String primaryKey : primaryKeys) {
                sb.append(primaryKey).append(" = #{").append(DBDToBeanUtils.parseFieldName(primaryKey)).append("} and ");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
    
    public void getMetaData(String tableName) throws SQLException {
        Connection connection = DBDToBeanContext.getDbdToBeanProperties().getConn();
        databaseMetaData = connection.getMetaData();
        PreparedStatement stmt = connection.prepareStatement("select * from " + tableName);
        ResultSet rs = stmt.executeQuery();
        if(rs.next()) {
            resultSetMetaData = rs.getMetaData();
        }
        DBDToBeanUtils.close(rs, stmt);
    }

    public List<String> getPrimaryKeys(String tableName){
        if(null != databaseMetaData) {
            try(ResultSet rs = databaseMetaData.getPrimaryKeys(null, null, tableName)){
                if (null == rs) {
                    return Collections.emptyList();
                }
                List<String> pks = new ArrayList<String>();
                while (rs.next()) {
                    String pkStr = rs.getString("COLUMN_NAME");
                    pks.add(pkStr);
                }
                return pks;
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        return Collections.emptyList();
    }
}
